package com.nali.dal.statement.upsert;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.annotation.PostConstruct;

import org.springframework.beans.factory.annotation.Autowired;

import com.nali.dal.dao.general.IGeneralOperateDao;
import com.nali.dal.expression.ExpressionDto;
import com.nali.dal.expression.ExpressionValue;
import com.nali.dal.expression.operate.Modifier;
import com.nali.dal.expression.query.Criteria;
import com.nali.dal.statement.query.ColumnUnitConfig;
import com.nali.dal.statement.query.ColumnUnitFactory;
import com.nali.dal.statement.query.ExpressionValueDesc;

/**
 * OperateUpserter<br>&nbsp;
 * upsert(default==true)
 * table string
 * modify
 * 		list
 * where
 * 		list
 * identitfiedBy(shortcut for where)
 * 		list
 * @author sam Created on 2011-4-28
 */
public class OperateUpserter implements Upserter {
	private static Pattern funPattern=Pattern.compile("(\\w+)\\((.+?)\\s*,\\s*(.+?)\\s*\\)");
	@Autowired
	private ColumnUnitFactory columnUnitFactory;
	private IGeneralOperateDao operateDao;
	private boolean upsert=true;
	private String table;
	private ExpressionValueConfig<Criteria> queryCfg;
	private ExpressionValueConfig<Modifier> modifyCfg;
	
	public int upsert(Map<String, Object> lineDatas) {
		ExpressionDto<Criteria> query = getQuery(lineDatas);
		List<ExpressionValue<Modifier>> modifiers = getModifiers(lineDatas);
		if(upsert) {
			return operateDao.upsert(table, query, modifiers);
		} else {
			return operateDao.update(table, query, modifiers);
		}
	}
	
	@SuppressWarnings("unchecked")
	@PostConstruct
	public void check() {
		if(table==null) {
			throw new IllegalArgumentException("no table");
		}
		if(queryCfg==null) {
			queryCfg = new ExpressionValueConfig<Criteria>(Collections.EMPTY_LIST);
		}
		if(modifyCfg==null) {
			throw new IllegalArgumentException("no modify");
		}
	}
	
	private List<ExpressionValue<Modifier>> getModifiers(Map<String, Object> lineDatas) {
		return modifyCfg.getExpressionValueList(lineDatas);
	}

	@SuppressWarnings("unchecked")
	private ExpressionDto<Criteria> getQuery(Map<String, Object> lineDatas) {
		List<ExpressionValue<Criteria>> traner = queryCfg.getExpressionValueList(lineDatas);
		return new ExpressionDto<Criteria>(Collections.EMPTY_LIST, traner);
	}

	public void setOperateDao(IGeneralOperateDao operateDao) {
		this.operateDao = operateDao;
	}

	public void setUpsert(boolean upsert) {
		this.upsert = upsert;
	}

	public void setModify(List<String> ops) {
		List<ExpressionValueDesc<Modifier>> conditionList = new ArrayList<ExpressionValueDesc<Modifier>>(ops.size());
		for (String op : ops) {
			Matcher matcher = funPattern.matcher(op);
			if(matcher.matches()) {
				Modifier modifier = tranToModifier(matcher.group(1));
				String prop = matcher.group(2);
				ColumnUnitConfig columnUnitConfig = columnUnitFactory.compile(matcher.group(3));
				conditionList.add(ExpressionValueDesc.getInstance(prop, modifier, columnUnitConfig));
			} else {
				throw new IllegalArgumentException("not supported syntax:" + op);
			}
		}
		modifyCfg = new ExpressionValueConfig<Modifier>(conditionList);
	
	}

	private Modifier tranToModifier(String fun) {
		try {
			return Modifier.valueOf(fun);
		} catch (Exception e) {
			throw new IllegalArgumentException("not supported function:" + fun);
		}
	}

	public void setWhere(List<String> conds) {
		checkQueryCfg();
		List<ExpressionValueDesc<Criteria>> conditionList = new ArrayList<ExpressionValueDesc<Criteria>>(conds.size());
		for (String cond : conds) {
			Matcher matcher = funPattern.matcher(cond);
			if(matcher.matches()) {
				Criteria criteria = tranToCriteria(matcher.group(1));
				String prop = matcher.group(2);
				ColumnUnitConfig columnUnitConfig = columnUnitFactory.compile(matcher.group(3));
				conditionList.add(ExpressionValueDesc.getInstance(prop, criteria, columnUnitConfig));
			} else {
				throw new IllegalArgumentException("not supported syntax:" + cond);
			}
		}
		queryCfg = new ExpressionValueConfig<Criteria>(conditionList);
	}

	private void checkQueryCfg() {
		if(queryCfg!=null) {
			throw new IllegalArgumentException("'identitfiedBy', 'modifyAll' or 'where' cannot be used more than once");
		}
	}
	
	@SuppressWarnings("unchecked")
	public void setModifyAll(boolean all) {
		if(all) {
			queryCfg = new ExpressionValueConfig<Criteria>(Collections.EMPTY_LIST);
		}
	}

	public void setIdentitfiedBy(List<String> identitfiedBy) {
		checkQueryCfg();
		List<ExpressionValueDesc<Criteria>> conditionList = new ArrayList<ExpressionValueDesc<Criteria>>(identitfiedBy.size());
		for (String id : identitfiedBy) {
			conditionList.add(ExpressionValueDesc.getInstance(id, Criteria.eq, columnUnitFactory.compile(id)));
		}
		queryCfg = new ExpressionValueConfig<Criteria>(conditionList);
	}
	private Criteria tranToCriteria(String fun) throws IllegalArgumentException {
		try {
			return Criteria.valueOf(fun);
		} catch (Exception e) {
			throw new IllegalArgumentException("not supported function:" + fun);
		}
	}

	public void setColumnUnitFactory(ColumnUnitFactory columnUnitFactory) {
		this.columnUnitFactory = columnUnitFactory;
	}

	public void setTable(String table) {
		this.table = table;
	}
}
